public class 反汇编器 {

    public 反汇编器(byte[] 字节码, int 字节码长度, Object[] 常量池) {
        this.字节码 = 字节码;
        this.字节码长度 = 字节码长度;
        this.常量池 = 常量池;
    }

    private final byte[] 字节码;
    private final int 字节码长度;
    private final Object[] 常量池;

    public StringBuilder 反汇编输出;

    public void 反汇编() {
        if (反汇编输出 == null) {
            反汇编输出 = new StringBuilder();
        } else {
            反汇编输出.setLength(0);
        }

        int 当前地址 = 0;
        while (当前地址 < 字节码长度) {
            if (!反汇编输出.isEmpty()) {
                反汇编输出.append(System.lineSeparator());
            }

            当前地址 = 反汇编指令(当前地址);
        }
    }

    public int 反汇编指令(int 当前地址) {
        return 反汇编指令(当前地址, false);
    }

    public int 反汇编指令(int 当前地址, boolean 清空旧输出){
        if (反汇编输出 == null) {
            反汇编输出 = new StringBuilder();
        }
        
        if (清空旧输出){
            反汇编输出.setLength(0);
        }

        var 指令码 = 字节码[当前地址];
        var 指令 = 字节码定义.指令集[指令码];
        反汇编输出.append(String.format("%04d: \t%-11s", 当前地址, 指令.名称));
        当前地址++;

        for (int i = 0; i < 指令.操作数个数; i++) {
            if (i > 0) {
                反汇编输出.append(", ");
            }

            var 操作数 = 字节码汇编器.读整数(字节码, 当前地址);
            当前地址 += 4;

            switch (指令.操作数[i]) {
                case 字节码定义.寄存器:
                    反汇编输出.append("r").append(操作数);
                    break;

                case 字节码定义.整数:
                    反汇编输出.append(操作数);
                    break;

                case 字节码定义.常量池索引:
                case 字节码定义.函数:
                    反汇编输出.append(取常量池操作数(操作数));
                    break;
            }
        }

        return 当前地址;
    }

    private String 取常量池操作数(int 常量池索引) {
        var 结果 = new StringBuilder();

        结果.append("#").append(常量池索引).append(":");
        ;
        var 常量 = 常量池[常量池索引];
        if (常量 instanceof String) {
            结果.append("\"").append(常量).append("\"");
        } else if (常量 instanceof 函数符号 函数符号) {
            结果.append(函数符号.名称).append("()@").append(函数符号.地址);
        }
        
        return 结果.toString();
    }
}
